/**************************************************************************************************
* Title: SumVector3.inl
* Author: Gael Huber
* Description: Defines 3D vector and related functions.
**************************************************************************************************/

/**************************************************************************************************
* Default constructor
**************************************************************************************************/
inline Vector3::Vector3(void)
	: x(0.0f), y(0.0f), z(0.0f)
{ }

/**************************************************************************************************
* Constructor from array
**************************************************************************************************/
inline Vector3::Vector3(const float* f)
	: x(f[0]), y(f[1]), z(f[2])
{ }

/**************************************************************************************************
* Copy constructor
**************************************************************************************************/
inline Vector3::Vector3(const Vector3& rhs)
	: x(rhs.x), y(rhs.y), z(rhs.z)
{ }

/**************************************************************************************************
* Constructor where x, y, and z values are specified
**************************************************************************************************/
inline Vector3::Vector3(float x, float y, float z)
	: x(x), y(y), z(z)
{ }

/**************************************************************************************************
* Destructor
**************************************************************************************************/
inline Vector3::~Vector3(void) 
{ }

/**************************************************************************************************
* Cast to non-const float array
**************************************************************************************************/
inline Vector3::operator float*(void) {
	return (float*)this;
}

/**************************************************************************************************
* Cast to const float array
**************************************************************************************************/
inline Vector3::operator const float*(void) {
	return (const float*)this;
}

/**************************************************************************************************
* Assignment operator
**************************************************************************************************/
inline Vector3& Vector3::operator=(const Vector3& rhs) {
	// Self-assignment guard
	if(this == &rhs) {
		return *this;
	}

	// Assign and return
	x = rhs.x;
	y = rhs.y;
	z = rhs.z;
	return *this;
}

/**************************************************************************************************
* Addition assignment operator
**************************************************************************************************/
inline Vector3& Vector3::operator+=(const Vector3& rhs) {
	x += rhs.x;
	y += rhs.y;
	z += rhs.z;
	return *this;
}

/**************************************************************************************************
* Subtraction assignment operator
**************************************************************************************************/
inline Vector3& Vector3::operator-=(const Vector3& rhs) {
	x -= rhs.x;
	y -= rhs.y;
	z -= rhs.z;
	return *this;
}

/**************************************************************************************************
* Subtraction assignment operator
**************************************************************************************************/
inline Vector3& Vector3::operator*=(float rhs) {
	x *= rhs;
	y *= rhs;
	z *= rhs;
	return *this;
}

/**************************************************************************************************
* Division assignment operator
**************************************************************************************************/
inline Vector3& Vector3::operator/=(float rhs) {
	const float f = 1.0f / rhs;
	x *= f;
	y *= f;
	z *= f;
	return *this;
}

/**************************************************************************************************
* Positive unary operator
**************************************************************************************************/
inline Vector3 Vector3::operator+(void) const {
	return *this;
}

/**************************************************************************************************
* Negative unary operator
**************************************************************************************************/
inline Vector3 Vector3::operator-(void) const {
	return Vector3(-x, -y, -z);
}

/**************************************************************************************************
* Addition operator
**************************************************************************************************/
inline Vector3 Vector3::operator+(const Vector3& rhs) const {
	return Vector3(x + rhs.x, y + rhs.y, z + rhs.z);
}

/**************************************************************************************************
* Subtraction operator
**************************************************************************************************/
inline Vector3 Vector3::operator-(const Vector3& rhs) const {
	return Vector3(x - rhs.x, y - rhs.y, z - rhs.z);
}

/**************************************************************************************************
* Multiplication operator
**************************************************************************************************/
inline Vector3 Vector3::operator*(float rhs) const {
	return Vector3(x * rhs, y * rhs, z * rhs);
}

/**************************************************************************************************
* Division operator
**************************************************************************************************/
inline Vector3 Vector3::operator/(float rhs) const {
	const float f = 1.0f / rhs;
	return Vector3(x * f, y * f, z * f);
}

/**************************************************************************************************
* Multiplication operator of the form lhs * rhs
**************************************************************************************************/
inline Vector3 operator*(float lhs, const Vector3& rhs) {
	return Vector3(lhs * rhs.x, lhs * rhs.y, lhs * rhs.z);
}

/**************************************************************************************************
* Equivalence
**************************************************************************************************/
inline bool Vector3::operator==(const Vector3& rhs) const {
	return x == rhs.x && y == rhs.y && z == rhs.z;
}

/**************************************************************************************************
* Non-equivalence
**************************************************************************************************/
inline bool Vector3::operator!=(const Vector3& rhs) const {
	return (x != rhs.x || y != rhs.y || z != rhs.z);
}

/**************************************************************************************************
* Length
**************************************************************************************************/
inline float Vec3Length(const Vector3* pV) {
	return sqrtf(pV->x*pV->x + pV->y*pV->y + pV->z*pV->z);
}

/**************************************************************************************************
* Squared length
**************************************************************************************************/
inline float Vec3LengthSqr(const Vector3* pV) {
	return pV->x*pV->x + pV->y*pV->y + pV->z*pV->z;
}

/**************************************************************************************************
* Dot
**************************************************************************************************/
inline float Vec3Dot(const Vector3* pV1, const Vector3* pV2) {
	return pV1->x*pV2->x + pV1->y*pV2->y + pV1->z*pV2->z;
}

/**************************************************************************************************
* Cross
**************************************************************************************************/
inline Vector3* Vec3Cross(Vector3* pOut, const Vector3* pV1, const Vector3* pV2) {
	pOut->x = pV1->y*pV2->z - pV1->z*pV2->y;
	pOut->y = pV1->z*pV2->x - pV1->x*pV2->z;
	pOut->z = pV1->x*pV2->y - pV1->y*pV2->x;
	return pOut;
}

/**************************************************************************************************
* Add
**************************************************************************************************/
inline Vector3* Vec3Add(Vector3* pOut, const Vector3* pV1, const Vector3* pV2) {
	pOut->x = pV1->x + pV2->x;
	pOut->y = pV1->y + pV2->y;
	pOut->z = pV1->z + pV2->z;
	return pOut;
}

/**************************************************************************************************
* Subtract
**************************************************************************************************/
inline Vector3* Vec3Sub(Vector3* pOut, const Vector3* pV1, const Vector3* pV2) {
	pOut->x = pV1->x - pV2->x;
	pOut->y = pV1->y - pV2->y;
	pOut->z = pV1->z - pV2->z;
	return pOut;
}

/**************************************************************************************************
* Minimize
**************************************************************************************************/
inline Vector3* Vec3Minimize(Vector3* pOut, const Vector3* pV1, const Vector3* pV2) {
	pOut->x = pV1->x < pV2->x ? pV1->x : pV2->x;
	pOut->y = pV1->y < pV2->y ? pV1->y : pV2->y;
	pOut->z = pV1->z < pV2->z ? pV1->z : pV2->z;
	return pOut;
}

/**************************************************************************************************
* Maximize
**************************************************************************************************/
inline Vector3* Vec3Maximize(Vector3* pOut, const Vector3* pV1, const Vector3* pV2) {
	pOut->x = pV1->x > pV2->x ? pV1->x : pV2->x;
	pOut->y = pV1->y > pV2->y ? pV1->y : pV2->y;
	pOut->z = pV1->z > pV2->z ? pV1->z : pV2->z;
	return pOut;
}

/**************************************************************************************************
* Scale
**************************************************************************************************/
inline Vector3* Vec3Scale(Vector3* pOut, const Vector3* pV, float s) {
	pOut->x = pV->x * s;
	pOut->y = pV->y * s;
	pOut->z = pV->z * s;
	return pOut;
}

/**************************************************************************************************
* Lerp. V1 + s * (V2 - V1)
**************************************************************************************************/
inline Vector3* Vec3Lerp(Vector3* pOut, const Vector3* pV1, const Vector3* pV2, float s) {
	pOut->x = pV1->x + s * (pV2->x - pV1->x);
	pOut->y = pV1->y + s * (pV2->y - pV1->y);
	pOut->z = pV1->z + s * (pV2->z - pV1->z);
	return pOut;
}

/**************************************************************************************************
* Normalize
**************************************************************************************************/
inline Vector3* Vec3Normalize(Vector3* pOut, const Vector3* pV) {
	float l = Vec3Length(pV);
	pOut->x = pV->x / l;
	pOut->y = pV->y / l;
	pOut->z = pV->z / l;
	return pOut;
}